import hashlib
import time
import tkinter as tk
import requests
import json

# 定义服务器的地址，这里假设服务器运行在本地的5000端口
SERVER_URL = 'http://127.0.0.1:5000'

# 全局变量，用于记录当前选择的功能和对应的输入框组件
current_function = None
function_entries = {}


# 函数用于添加新用户（矿工）
def add_user():
    """
    此函数实现添加新用户（矿工）的功能。
    1. 首先从名为name_entry的输入框获取用户输入的名字。
    2. 判断名字长度是否小于10个字符，如果是，则在界面的message_label标签上显示错误提示并返回，不进行后续操作。
    3. 向服务器的/register端点发送POST请求，请求中携带用户名字信息（以JSON格式）。
    4. 根据服务器返回的JSON数据中的'message'字段判断是否注册成功：
        - 如果'message'为'注册成功'，则在message_label标签上显示注册成功及返回的地址信息。
        - 否则，显示服务器返回的错误消息。
    """
    name = function_entries.get("name", "").get()
    if len(name) < 10:
        message_label.config(text="特别名字长度太短，请重试！")
        return
    response = requests.post(f'{SERVER_URL}/register', json={'name': name})
    data = response.json()
    if data['message'] == '注册成功':
        message_label.config(text=f"注册成功！地址：{data['address']}")
    else:
        message_label.config(text=data['message'])


# 函数用于查询指定地址的余额
def display_balance():
    """
    该函数用于查询余额操作，具体步骤如下：
    1. 从address_entry输入框获取要查询余额的地址信息。
    2. 向服务器的/balance端点发送POST请求，请求中携带地址信息（以JSON格式）。
    3. 解析服务器返回的JSON数据：
        - 如果数据中包含'balance'字段，则在message_label标签上显示余额信息。
        - 否则，显示服务器返回的错误消息。
    """
    address = function_entries.get("address", "").get()
    response = requests.post(f'{SERVER_URL}/balance', json={'address': address})
    data = response.json()
    if 'balance' in data:
        message_label.config(text=f"余额：{data['balance']}")
    else:
        message_label.config(text=data['message'])


# 函数用于实现转账功能
def transfer():
    """
    此函数实现转账的功能，执行流程如下：
    1. 分别从对应的输入框获取发送者地址、接收者地址、转账数量以及发送者名字信息。
    2. 先对发送者名字进行非空验证，如果为空，则在message_label标签上显示错误提示并返回，不执行后续转账操作。
    3. 构造包含转账相关信息的数据字典，包含'sender_address'、'recipient_address'、'amount'、'sender_name'这些键值对。
    4. 向服务器的/transfer端点发送POST请求，请求中携带上述构造的数据字典（以JSON格式）。
    5. 将服务器返回的消息显示在message_label标签上。
    """
    sender_address = function_entries.get("sender_address", "").get()
    recipient_address = function_entries.get("recipient_address", "").get()
    amount = function_entries.get("amount", "").get()
    sender_name = function_entries.get("sender_name", "").get()
    if sender_name == "":
        message_label.config(text="特别名字不能为空！")
        return
    try:
        amount = int(amount)
    except ValueError:
        message_label.config(text="转账数量必须为整数，请重试！")
        return
    data = {
        'sender_address': sender_address,
        'recipient_address': recipient_address,
        'amount': amount,
        'sender_name': sender_name
    }
    response = requests.post(f'{SERVER_URL}/transfer', json=data)
    message_label.config(text=response.json()['message'])


# 函数用于执行挖矿操作
def mine_block():
    """
    本函数实现挖矿功能，具体逻辑如下：
    1. 先获取miner_address_entry输入框中的矿工地址信息。
    2. 构造包含矿工地址的字典mdata，键为'miner_address'。
    3. 向服务器的/validate_mine端点发送POST请求，携带mdata（以JSON格式）进行挖矿前的验证操作。
    4. 解析服务器返回的JSON数据：
        - 如果数据中包含'proof_no'字段，则进入挖矿循环：
            - 在循环中，不断递增proof_no的值。
            - 根据特定规则构造用于计算哈希值的字符串guess，将其编码后通过hashlib.sha256计算哈希值。
            - 当计算出的哈希值的前四位为'0000'时，认为挖矿成功，记录当前时间作为结束时间。
            - 向服务器的/mine端点发送POST请求，携带矿工地址和验证后的proof_no（以JSON格式）。
            - 根据服务器返回的消息判断是否挖矿成功，并在message_label标签上显示相应信息以及挖矿用时等内容，然后退出循环。
        - 如果数据中不包含'proof_no'字段，则直接将服务器返回的消息显示在message_label标签上。
    """
    miner_address = function_entries.get("miner_address", "").get()
    mdata = {'miner_address': miner_address}
    response = requests.post(f'{SERVER_URL}/validate_mine', json=mdata)
    data = response.json()
    if 'proof_no' in data:
        proof_no = data['proof_no']
        start_time = time.time()
        while True:
            proof_no += 1
            guess = f'{proof_no}{data["proof_no"]}'.encode()
            guess_hash = hashlib.sha256(guess).hexdigest()
            if guess_hash[:4] == "0000":
                end_time = time.time()
                response = requests.post(f'{SERVER_URL}/mine', json={'miner_address': miner_address,'miner_proof_no': proof_no})
                data = response.json()
                if data['message'] == '挖矿成功':
                    message_label.config(text=f"挖矿成功！用时{(end_time - start_time):.2f}秒。\n{data['block']}")
                else:
                    message_label.config(text=data['message'])
                break
    else:
        message_label.config(text=data['message'])


# 函数用于查询所有矿工的信息
def list_miners():
    """
    该函数实现查询所有矿工信息的功能，操作步骤如下：
    1. 向服务器的/miners端点发送GET请求。
    2. 解析服务器返回的JSON数据：
        - 如果数据中包含'miners'字段（预期是一个包含多个矿工信息的列表），则遍历该列表：
            - 对于每个矿工信息，拼接其地址和余额信息到miner_info字符串中。
            - 最后将拼接好的miner_info字符串显示在message_label标签上，展示所有矿工的地址和余额情况。
        - 如果数据中不包含'miners'字段，则显示服务器返回的错误消息。
    """
    response = requests.get(f'{SERVER_URL}/miners')
    data = response.json()
    if 'miners' in data:
        miners = data['miners']
        miner_info = ""
        for miner in miners:
            miner_info += f"{miner['address']}  - {miner['balance']}\n"
        message_label.config(text=miner_info)
    else:
        message_label.config(text=data['message'])


# 函数用于找回用户地址
def user_address():
    """
    此函数用于找回用户地址，具体执行过程如下：
    1. 从name_entry输入框获取用户名字信息。
    2. 向服务器的/forgot端点发送POST请求，请求中携带名字信息（以JSON格式）。
    3. 解析服务器返回的JSON数据：
        - 如果数据中包含'address'字段，则在message_label标签上显示地址信息。
        - 否则，显示服务器返回的错误消息。
    """
    name = function_entries.get("name", "").get()
    response = requests.post(f'{SERVER_URL}/forgot', json={'name': name})
    data = response.json()
    if 'address' in data:
        message_label.config(text=f"地址：{data['address']}")
    else:
        message_label.config(text=data['message'])


# 创建主窗口
root = tk.Tk()
root.title("马币系统 (Developed with 李云峰 by 代阳 Company)")
root.geometry("600x400")
root.configure(bg="#F0F0F0")

# 创建用于显示各种操作的反馈消息的标签，设置为可复制文本等属性
message_label = tk.Label(root, text="", bg="white", fg="#333333", font=("Arial", 12), justify="left", wraplength=500,
                         relief="solid", borderwidth=1)
message_label.pack(pady=10, padx=10, fill=tk.BOTH, expand=True)
message_label.bind("<Button-1>", lambda e: root.clipboard_clear())
message_label.bind("<Button-1>", lambda e: root.clipboard_append(message_label.cget("text")), add="+")

# 创建选择功能的按钮框架
button_frame = tk.Frame(root, bg="#F0F0F0")
button_frame.pack(pady=10)

# 创建功能按钮，点击按钮时设置当前选择的功能，并根据功能显示对应的输入框
add_user_button = tk.Button(button_frame, text="添加新矿工", command=lambda: set_current_function(add_user, ["name"]),
                            bg="#4CAF50", fg="white", font=("Arial", 12), padx=10, pady=5)
add_user_button.pack(side=tk.LEFT, padx=5)

display_balance_button = tk.Button(button_frame, text="查询余额", command=lambda: set_current_function(display_balance, ["address"]),
                                    bg="#2196F3", fg="white", font=("Arial", 12), padx=10, pady=5)
display_balance_button.pack(side=tk.LEFT, padx=5)

transfer_button = tk.Button(button_frame, text="转账", command=lambda: set_current_function(transfer, ["sender_address", "recipient_address", "amount", "sender_name"]),
                            bg="#FF9800", fg="white", font=("Arial", 12), padx=10, pady=5)
transfer_button.pack(side=tk.LEFT, padx=5)

mine_block_button = tk.Button(button_frame, text="挖矿", command=lambda: set_current_function(mine_block, ["miner_address"]),
                              bg="#795548", fg="white", font=("Arial", 12), padx=10, pady=5)
mine_block_button.pack(side=tk.LEFT, padx=5)

list_miners_button = tk.Button(button_frame, text="查询所有矿工信息", command=lambda: set_current_function(list_miners, []),
                               bg="#607D8B", fg="white", font=("Arial", 12), padx=10, pady=5)
list_miners_button.pack(side=tk.LEFT, padx=5)

user_address_button = tk.Button(button_frame, text="忘记地址", command=lambda: set_current_function(user_address, ["name"]),
                                bg="#9C27B0", fg="white", font=("Arial", 12), padx=10, pady=5)
user_address_button.pack(side=tk.LEFT, padx=5)

exit_button = tk.Button(button_frame, text="退出程序", command=root.quit, bg="#F44336", fg="white", font=("Arial", 12),
                        padx=10, pady=5)
exit_button.pack(side=tk.LEFT, padx=5)

def execute_function():
    """
    执行当前选择的功能，如果没有选择功能则提示相应信息。
    """
    global current_function
    if current_function:
        current_function()
    else:
        message_label.config(text="请先选择功能")

# 创建执行功能的按钮
execute_button = tk.Button(root, text="执行", command=execute_function, bg="#00BCD4", fg="white", font=("Arial", 12),
                           padx=10, pady=5)
execute_button.pack(pady=10)


# def set_current_function(func, entries):
#     """
#     设置当前选择的功能以及对应的输入框列表，并显示相应的输入框。
#     :param func: 要执行的功能函数
#     :param entries: 该功能所需的输入框名称列表
#     """
#     global current_function, function_entries
#     current_function = func
#     function_entries = {}
#     # 先清空之前可能显示的输入框
#     for widget in root.winfo_children():
#         if isinstance(widget, tk.Entry):
#             widget.destroy()
#     # 根据传入的entries列表创建并显示对应的输入框
#     row = 0
#     for entry_name in entries:
#         label = tk.Label(root, text=f"{entry_name}：", bg="#F0F0F0", fg="#333333", font=("Arial", 12))
#         label.pack()
#         entry = tk.Entry(root, font=("Arial", 12))
#         entry.pack()
#         function_entries[entry_name] = entry
#         row += 1
        
def set_current_function(func, entries):
    """
    设置当前选择的功能以及对应的输入框列表，并显示相应的输入框。
    :param func: 要执行的功能函数
    :param entries: 该功能所需的输入框名称列表
    """
    global current_function, function_entries
    current_function = func
    function_entries = {}

    # 先清空之前所有已显示的输入框和对应的标签，但保留 message_label
    for widget in root.winfo_children():
        if (isinstance(widget, tk.Entry) or isinstance(widget, tk.Label)) and widget!= message_label:
            widget.destroy()

    # 根据传入的entries列表创建并显示对应的输入框和标签
    row = 0
    for entry_name in entries:
        label = tk.Label(root, text=f"{entry_name}：", bg="#F0F0F0", fg="#333333", font=("Arial", 12))
        label.pack()
        entry = tk.Entry(root, font=("Arial", 12))
        entry.pack()
        function_entries[entry_name] = entry
        row += 1
        
root.mainloop()